﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections;
using System.Reflection;
using System.Collections.Specialized;
using Newtonsoft.Json.Converters;

/// <summary>
/// 日志操作类
/// </summary>
public class LogUtils
{

    private static object locker = new object();
    /// <summary>
    /// 获取请求相关信息
    /// </summary>
    /// <param name="level">日志级别</param>
    /// <returns></returns>
    private static Dictionary<string, object> GetRequestData(LogLevel level)
    {
        Dictionary<string, object> dictData = new Dictionary<string, object>();
        if (HttpContext.Current != null && HttpContext.Current.Request != null)
        {
            HttpRequest Request = HttpContext.Current.Request;
            if (Request.Url != null)
            {
                dictData.Add("URI", Request.Url.AbsoluteUri);
            }
            if (Request.UrlReferrer != null)
            {
                dictData.Add("UrlReferrer", Request.UrlReferrer.AbsoluteUri);
            }
            if (Request.ServerVariables != null)
            {
                #region 获取IP地址
                Regex regex = new Regex("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}");
                string str_Ip = string.Empty;
                if (!string.IsNullOrEmpty(Request.ServerVariables["REMOTE_ADDR"]))
                {
                    str_Ip = Request.ServerVariables["REMOTE_ADDR"];
                }
                else if (!string.IsNullOrEmpty(Request.ServerVariables["HTTP_CDN_SRC_IP"]) && regex.IsMatch(Request.ServerVariables["HTTP_CDN_SRC_IP"]))
                {
                    str_Ip = Request.ServerVariables["HTTP_CDN_SRC_IP"];
                }
                else if (!string.IsNullOrEmpty(Request.ServerVariables["HTTP_X_FORWARDED_FOR"]) && regex.IsMatch(Request.ServerVariables["HTTP_X_FORWARDED_FOR"]))
                {
                    str_Ip = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                }
                string[] ip = str_Ip.Split(',');
                #endregion
                dictData.Add("IP", ip[0]);
            }
            dictData.Add("UserAgent", Request.UserAgent);
            if (level == LogLevel.Warn)
            {
                dictData.Add("POST信息", Request.Form);
            }
            else if (level == LogLevel.Error)
            {
                dictData.Add("POST信息", Request.Form);

                if (Request.Cookies != null && Request.Cookies.Count > 0)
                {
                    Dictionary<string, object> dictTemp = new Dictionary<string, object>();
                    foreach (string key in Request.Cookies.AllKeys)
                    {
                        string value = Request.Cookies[key].Values[null];
                        if (Request.Cookies[key].Values.Count > 1)// Request.Cookies[key].Values => System.Web.HttpValueCollection 类型
                        {
                            Request.Cookies[key].Values.Remove(null);
                            Dictionary<string, string> kvDict = new Dictionary<string, string>();
                            kvDict.Add("@null", value);
                            var nvcTemp = Request.Cookies[key].Values;
                            foreach (var ckKey in nvcTemp.AllKeys)
                            {
                                kvDict.Add(ckKey, nvcTemp[ckKey]);
                            }
                            dictTemp.Add(key, kvDict);
                        }
                        else
                        {
                            dictTemp.Add(key, value);
                        }
                    }
                    dictData.Add("Cookie信息", dictTemp);
                }
            }
        }
        return dictData;
    }

    /// <summary>
    /// 写入日志
    /// </summary>
    /// <param name="logName">日志名称</param>
    /// <param name="developer">开发记录者</param>
    /// <param name="level">日志级别</param>
    /// <param name="detail">日志详情</param>
    /// <param name="Added">记录时间</param>
    private static void Write(string logName, Developer developer, LogLevel level, Dictionary<string, object> dictDetail, DateTime Added)
    {
        Log log = new Log();
        log.LogName = logName;
        log.Level = level;
        log.Developer = developer;
        if (HttpContext.Current != null && HttpContext.Current.Server != null)
        {
            log.ServerInfo = HttpContext.Current.Server.MachineName;
        }
        log.Added = Added;
        log.Detail = dictDetail.Union(GetRequestData(level)).ToDictionary(k => k.Key, v => v.Value);

        //todo :可以将日志写入 文件、数据库、MongoDB
        //这里写入根目录 log文件夹
        string logText = GetJsonFromObject(log) + "\r\n";
        string fileName = DateTime.Now.ToString("yyyyMMdd") + ".log";
        string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log");
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        fileName = Path.Combine(dir, fileName);
        File.AppendAllText(fileName, logText, Encoding.UTF8);
        File.AppendAllText(fileName, "--------------------------------------------------------------------------------------------------------------------------------\r\n", Encoding.UTF8);
    }

    /// <summary>
    /// 写入Info 日志
    /// </summary>
    /// <param name="logName">日志名称</param>
    /// <param name="developer">开发记录者</param>
    /// <param name="Info_objs">日志内容</param>
    public static void LogInfo(string logName, Developer developer, params object[] Info_objs)
    {
        lock (locker)
        {
            Dictionary<string, object> dictDetails = new Dictionary<string, object>();
            if (Info_objs != null && Info_objs.Length > 0)
            {
                dictDetails.Add("Info", Info_objs);
            }
            Write(logName, developer, LogLevel.Info, dictDetails, DateTime.Now);
        }
    }


    /// <summary>s
    /// 写入带 堆栈执行 的Info 日志
    /// </summary>
    /// <param name="logName">日志名称</param>
    /// <param name="developer">开发记录者</param>
    /// <param name="Info_objs">日志内容</param>
    public static void LogWrite(string logName, Developer developer, params object[] Info_objs)
    {
        lock (locker)
        {
            Dictionary<string, object> dictDetails = new Dictionary<string, object>();
            System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true);
            System.Diagnostics.StackFrame frame = stack.GetFrame(0);
            string execFile = frame.GetFileName();
            string fullName = frame.GetMethod().DeclaringType.FullName;
            string methodName = frame.GetMethod().Name;
            int execLine = frame.GetFileLineNumber();
            dictDetails.Add("文件路径", execFile);
            dictDetails.Add("类全命名", fullName);
            dictDetails.Add("执行方法", methodName);
            dictDetails.Add("当前行号", execLine);

            if (Info_objs != null && Info_objs.Length > 0)
            {
                dictDetails.Add("Info", Info_objs);
            }
            Write(logName, developer, LogLevel.Info, dictDetails, DateTime.Now);
        }
    }

    /// <summary>
    /// 写入Warn 日志
    /// </summary>
    /// <param name="logName">日志名称</param>
    /// <param name="developer">开发记录者</param>
    /// <param name="Info_objs">日志内容</param>
    public static void LogWarn(string logName, Developer developer, params object[] Info_objs)
    {
        lock (locker)
        {
            Dictionary<string, object> dictDetails = new Dictionary<string, object>();
            System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true);
            System.Diagnostics.StackFrame frame = stack.GetFrame(0);
            string execFile = frame.GetFileName();
            string fullName = frame.GetMethod().DeclaringType.FullName;
            string methodName = frame.GetMethod().Name;
            int execLine = frame.GetFileLineNumber();
            dictDetails.Add("文件路径", execFile);
            dictDetails.Add("类全命名", fullName);
            dictDetails.Add("执行方法", methodName);
            dictDetails.Add("当前行号", execLine);

            if (Info_objs != null && Info_objs.Length > 0)
            {
                dictDetails.Add("Info", Info_objs);
            }
            Write(logName, developer, LogLevel.Warn, dictDetails, DateTime.Now);
        }
    }

    /// <summary>
    /// 写入 Errorr日志
    /// </summary>
    /// <param name="logName">日志名称</param>
    /// <param name="developer">开发记录者</param>
    /// <param name="ex">异常对象(可为null)</param>
    /// <param name="ext_InfoObjs">日志内容</param>
    public static void LogError(string logName, Developer developer, Exception ex, params object[] ext_InfoObjs)
    {
        lock (locker)
        {
            Dictionary<string, object> dictDetails = new Dictionary<string, object>();
            if (ex != null)
            {
                dictDetails.Add("Exception", ex);
            }
            if (ex.InnerException != null)
            {
                dictDetails.Add("InnerException", ex.InnerException);
            }
            if (ext_InfoObjs != null && ext_InfoObjs.Length > 0)
            {
                dictDetails.Add("Ext_Info", ext_InfoObjs);
            }
            Write(logName, developer, LogLevel.Error, dictDetails, DateTime.Now);
        }
    }


    #region  private 反射 对象

    private static string GetJsonFromObject(object obj, bool isFormat = true)
    {
        if (obj == null)
        {
            return string.Empty;
        }
        string result = string.Empty;

        //序列化 对象成Json字符串
        Newtonsoft.Json.Formatting format = isFormat ? Newtonsoft.Json.Formatting.Indented : Newtonsoft.Json.Formatting.None;

        //时间格式化
        IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss:fff" };
        //枚举类型格式化
        StringEnumConverter emConverter = new StringEnumConverter();

        List<Newtonsoft.Json.JsonConverter> lstJCvtr = new List<Newtonsoft.Json.JsonConverter>();
        lstJCvtr.Add(dtConverter);
        lstJCvtr.Add(emConverter);

        result = Newtonsoft.Json.JsonConvert.SerializeObject(obj, format, lstJCvtr.ToArray());

        return result;
    }

    #endregion
}


/// <summary>
/// 程序日志
/// </summary>
public class Log
{
    public Guid Id { get { return Guid.NewGuid(); } }

    /// <summary>
    /// 日志名称
    /// </summary>
    public string LogName { get; set; }

    /// <summary>
    /// 日志级别
    /// </summary>
    public LogLevel Level { get; set; }

    /// <summary>
    /// 当前记录日志者
    /// </summary>
    public Developer Developer { get; set; }

    /// <summary>
    /// 服务器信息（如：服务器名称、服务器IP）
    /// </summary>
    public string ServerInfo { get; set; }

    /// <summary>
    /// 日志详细内容
    /// </summary>
    public dynamic Detail { get; set; }

    /// <summary>
    /// 日志时间
    /// </summary>     
    public DateTime Added { get; set; }

}

/// <summary>
/// 日志级别
/// </summary>
public enum LogLevel
{
    Info = 0,
    Warn = 1,
    Error = 2
}

/// <summary>
/// 日志记录开发者
/// </summary>
public enum Developer
{
    /// <summary>
    /// 系统默认
    /// </summary>
    SysDefault = 0,

    /// <summary>
    /// 其他用户
    /// </summary>
    MJ = 115
}
